
using namespace std;

#include "Partida.h"

Partida *Partida::instance = NULL;

Partida *Partida::getInstance(){
    if(!instance){
        instance = new Partida();
        return instance;
    } else {
        return instance;
    }
}

Partida::Partida(){
	//Activo los mutexs.
	this->mutexListaElementos 			= new ThreadMutex();
	this->mutexColaAcciones 			= new ThreadMutex();
	this->mutexColaRespuestaAcciones	= new ThreadMutex();
	this->mutexHayRespuestas			= new ThreadMutex();
	this->mutexHayAcciones				= new ThreadMutex();

	//Arrancan tomados, hasta que se inserten acciones o respuestas.
	this->mutexHayRespuestas->P();
	this->mutexHayAcciones->P();

	/* La partida arranca parada hasta que se puedan ejecutar acciones */
	this->stoped = CONST_TRUE;
};

/* Metodo que inicializa la partida, y envia a los jugadores que numero de jugador son y el nombre de su contrincante */
bool Partida::iniciarPartida(Socket *conexion){
	//Activo el flag de activo.
	instance->activo = true;

	//Inicializo los id de los elementos únicos.
	instance->idRalph	= CONST_MENOS_UNO;

	//Genero los objetos iniciales vacios y no vivos, para luego sortear por tramo su estado.
	instance->crearElementosIniciales();

	/* Envio la accion al cliente. */
	if(!instance->notificarInicioPartida(conexion))
		return CONST_FALSE;

	/* Actualizo el ranking y puntaje de los jugadores desde la shm */
	refreshStatsJugadores(CONST_TRUE, CONST_TRUE);

	/* Inicializo el primer nivel */
	instance->inicializarNivel(CONST_UNO);

	return CONST_TRUE;
};

/* Inicializa los objetos del nivel en cuestion como constantes de dificultad y numero de nivel.*/
void Partida::inicializarNivel(unsigned short int nivel){
	/* Busco las parametrizaciones de dificultad para este nivel */
	ParametrosDificultad::getInstance()->setParmsPorNivel(nivel, &maxMovSinLanzar, &maxCiclosSinAves, &cantBloqueosAQuitarH, &cantBloqueosAQuitarV, &maxCiclosSinTortas, &cantTramosNivelActual);

	/* Inicializo el numero de nivel y de tramo inicial. */
	instance->nivelActual		    = nivel;
	instance->tramoActual			= CONST_UNO; 				/* El primer tramo a setear es el 1 */

	/* Encolo la accion para que los jugadores la ejecuten tambien. */
	t_accion action;
	action.id_accion  = ACC_INIT_NIVEL;
	action.id_entidad = nivel;
	instance->encolarRespuestaJugadores(action);

	/* Inicio el primer tramo */
	instance->inicializarTramo(instance->tramoActual);
}

//Inicializa los elementos del tramo, sorteando ya los vidrios rotos y los bloqueos.
void Partida::inicializarTramo(unsigned int tramo){
	//TODO:
	/* Inicializo los id de los elementos únicos. */
	instance->idAve		= CONST_MENOS_UNO;
	instance->idTorta   = CONST_MENOS_UNO;

	/* Inicializo contadores e indicadores de sentido de desplazamiento. */
	instance->sentidoRalph		  = CONST_UNO;
	instance->cantLadrillos		  = CONST_CERO;
	instance->cantVidriosRotos 	  = CONST_CERO;
	instance->cantCiclosSinAves	  = CONST_CERO;
	instance->cantCiclosSinTortas = CONST_CERO;

	/* Seteo el numero de tramo */
	instance->tramoActual	   		= tramo;

	/* Sorteo las ventanas rotas y bloqueos y creo objetos iniciales */
	int semilla = time(NULL);
	instance->sortearEstadosTramo(semilla);

	/* Reseteo el estado de los elementos que persisten toda la partida o nivel */
	resetObjetosPorTramo();

	/* Encolo la accion para que los jugadores la ejecuten tambien con la misma semilla. */
	notificarInicioTramo(tramo, semilla);

	/* Por ultimo vuelvo a poner a andar la partida */
	instance->setStoped(CONST_FALSE);
};

//Setters
void Partida::setId(unsigned short int id){
	instance->getPartida()->partidaId = id;
}
void Partida::setStoped(bool stoped){
	instance->stoped = stoped;
}

//------------------------------------------------------Notificaciones a los jugadores-----------------------------------------------//

/* Se encarga de notificar a los clientes que comenzo la partida indicandoles que numero de jugador es y el nombre del otro jugador. */
bool Partida::notificarInicioPartida(Socket *conexion){
	//Accion para el jugador 1 con el nombre del jugador2
	t_accion actionJ1;
	actionJ1.id_accion 	= ACC_INIT_PARTIDA;
	actionJ1.id_entidad	= CONST_ID_FELIX1;
	strcpy(actionJ1.msj, instance->getPartida()->jugador2.nombre);

	//Accion para el jugador2 con el nombre del jugador1
	t_accion actionJ2;
	actionJ1.id_accion 	= ACC_INIT_PARTIDA;
	actionJ1.id_entidad	= CONST_ID_FELIX2;
	strcpy(actionJ2.msj, instance->getPartida()->jugador1.nombre);

	if(conexion->write(((void *)&actionJ1), instance->getPartida()->jugador1.socket_id) == -1||
	   conexion->write(((void *)&actionJ2), instance->getPartida()->jugador2.socket_id) == -1 ){
		cout << "[ERROR] - No se pudo enviar una accion a alguno de los jugadores. " << endl;
		return CONST_FALSE;
	}

	return CONST_TRUE;
};

/* Envia a los jugadores la accion de finalizar partida */
void Partida::notificarFinPartida(){
	t_accion a;
	a.id_accion = ACC_FIN_PARTIDA;
	a.id_entidad= instance->partida.partidaId;
	encolarRespuestaJugadores(a);
}

/* Notifica a los jugadores que se actualizo el estado de los jugadores */
void Partida::notificarActEstadoJugadores(bool puntaje, bool ranking){
	if(puntaje)
		notificarActPuntajeJugadores();

	if(ranking)
		notificarActRankingJugadores();
};

/* Notifica a los jugadores que se actualizo el ranking de los jugadores */
void Partida::notificarActRankingJugadores(){
	/* Encolo la accion para enviarla a los jugadores y que actualicen la informacion */
	t_accion a;

	/* Primero jugador1 */
	a.id_accion  = ACC_ACT_RANK;
	a.id_entidad = CONST_ID_FELIX1;
	a.mov_x		 = instance->partida.jugador1.ranking;
	encolarRespuestaJugadores(a);

	/* Despues jugador2 */
	a.id_accion  = ACC_ACT_RANK;
	a.id_entidad = CONST_ID_FELIX2;
	a.mov_x		 = instance->partida.jugador2.ranking;
	encolarRespuestaJugadores(a);
};

/* Notifica a los jugadores que se actualizo el puntaje de los jugadores */
void Partida::notificarActPuntajeJugadores(){
	/* Encolo la accion para enviarla a los jugadores y que actualicen la informacion */
	t_accion a;

	/* Primero jugador1 */
	a.id_accion  = ACC_ACT_PUNT;
	a.id_entidad = CONST_ID_FELIX1;
	a.mov_x		 = instance->partida.jugador1.puntaje;
	encolarRespuestaJugadores(a);

	/* Despues jugador2 */
	a.id_accion  = ACC_ACT_PUNT;
	a.id_entidad = CONST_ID_FELIX2;
	a.mov_x		 = instance->partida.jugador2.puntaje;
	encolarRespuestaJugadores(a);
};

/* Notifica a los jugadores todas las acciones que desencadena la reparacion de un vidrio */
void Partida::notificarReparacionVidrios(Felix *felix, Vidrio *vidrio1, Vidrio *vidrio2){
	/* Encolo las acciones para devolversela al jugador y que las impacte. */
	t_accion action;

	/* 1- Felix reparando el vidrio */
	action.id_accion  = ACC_REPARAR;
	action.id_entidad = felix->getId();
	instance->encolarRespuestaJugadores(action);

	/* 2,3- Reparar los vidrios */
	action.id_accion  = ACC_REPARAR;
	action.id_entidad = vidrio1->getId();
	instance->encolarRespuestaJugadores(action);
	action.id_entidad = vidrio2->getId();
	instance->encolarRespuestaJugadores(action);

	/* 4- Actualizar puntaje de felix*/
	action.id_accion  = ACC_ACT_PUNT;
	action.id_entidad = felix->getId();
	action.mov_x	  = felix->getPuntos();
	instance->encolarRespuestaJugadores(action);
};

/* Notifica a los jugadores que comienza un nuevo tramo enviando el numero de tramo y la semilla para el sorteo de vidrios y bloqueos */
void Partida::notificarInicioTramo(unsigned short int tramo, int semilla){
	t_accion action;
	action.id_accion  = ACC_INIT_TRAMO;
	action.id_entidad = tramo;
	action.mov_x 	  = semilla;
	instance->encolarRespuestaJugadores(action);
};


//-------------------------------------------Utilitarios--------------------------------------------------//

//Agrega la t_accion recibida por parámetro, a la cola de acciones a ejecutar.
void Partida::encolarAccionAProcesar(t_accion action){
	//Encolo la t_accion.
	instance->mutexColaAcciones->P();
	instance->colaAcciones.push(action);
	instance->mutexColaAcciones->V();

	//Si es el primero que cargo, libero el mutex. d
	if(((int)instance->colaAcciones.size()) == CONST_UNO){
		instance->mutexHayAcciones->V();
	}
};

//Va sacando de la cola de acciones, para ejecutarlas.
t_accion Partida::desencolarAccionAProcesar(){
	t_accion a;

	//Espero a que halla acciones encoladas.
	instance->mutexHayAcciones->P();

	//Cuando hay, desencolo.
	instance->mutexColaAcciones->P();
	a = instance->colaAcciones.front();
	instance->colaAcciones.pop();
	instance->mutexColaAcciones->V();

	//Si no quedan mas datos, lo dejo tomado hasta que el que carge la cola lo libere.
	if(instance->colaAcciones.size() != (unsigned int)CONST_CERO)
		instance->mutexHayAcciones->V();

	return a;
};

/*Va tomando las accciones de la cola, y por cada una busca el comando a ejecutar para atenderla y lo ejecuta */
void Partida::procesarAccion(){
    t_accion action = desencolarAccionAProcesar();
    ComandoServidor *cS = InterpreteComandosServer::getInstance()->getComando(&action);
	cS->execute(&action);
};

//Agrega la t_accion recibida por parámetro, a la cola de acciones a ejecutar.
void Partida::encolarRespuestaJugadores(t_accion action){
	//Encola la respuesta.
	instance->mutexColaRespuestaAcciones->P();
	colaRespuestaAcciones.push(action);
	instance->mutexColaRespuestaAcciones->V();

	//Si es el primero que cargo, libero el mutex.
	if(instance->colaRespuestaAcciones.size() == (unsigned int)CONST_UNO)
		instance->mutexHayRespuestas->V();
};

//Va sacando de la cola de acciones, para ejecutarlas.
t_accion Partida::desencolarRespuestaJugadores(){
	t_accion a;
	//Espero a que halla respuestas para desencolar.
	instance->mutexHayRespuestas->P();

	//Cuando hay, desencolo.
	instance->mutexColaRespuestaAcciones->P();
	a = instance->colaRespuestaAcciones.front();
	instance->colaRespuestaAcciones.pop();
	instance->mutexColaRespuestaAcciones->V();

	//Si no quedan mas datos, lo dejo tomado hasta que el que carge la cola lo libere.
	if(instance->colaRespuestaAcciones.size() != (unsigned int)CONST_CERO)
		instance->mutexHayRespuestas->V();

	return a;
};

/* Actualiza los datos de la partida en la memoria compartida para el servidor de torneo. */
void Partida::actualizarJugadorServidorTorneo(Felix *jugador){
	/* Traigo el puntero a la partida en shm */
	t_partida *partidaShm = getPartidaCompartida();

	if(partidaShm != NULL){
		/* Me fijo que jugador es el que cambio */
		if(jugador->getId()==CONST_ID_FELIX1){
			/* Actualizar el dato partida local para luego impactar el del torneo */
			instance->partida.jugador1.puntaje = jugador->getPuntos();
			/* Actualizo en la shm con torneo */
			partidaShm->jugador1.puntaje  = instance->partida.jugador1.puntaje;
			partidaShm->jugador1.conectado= instance->partida.jugador1.conectado;
		}else{
			/* Actualizar el dato partida local para luego impactar el del torneo */
			instance->partida.jugador2.puntaje = jugador->getPuntos();
			/* Actualizo en la shm con torneo */
			partidaShm->jugador2.puntaje  = instance->partida.jugador2.puntaje;
			partidaShm->jugador2.conectado= instance->partida.jugador2.conectado;
		}

		actualizarMemoriaCompartida(partidaShm);
	}else{
		cout << "[ERROR] - No se puede acceder a la memoria compartida, cerrando servidor partida." << endl;
		exit(0);
	}
};

/* Trae el puntero a la partida almacenada en shm con el servidor torneo. */
t_partida *Partida::getPartidaCompartida(){
	//TODO: Retornar un puntero a la estructura de partida que esta en memoria compartida
	return new t_partida;
};

/* Actualiza los datos de la partida almacenada en shm con el servidor torneo. */
void Partida::actualizarMemoriaCompartida(t_partida *partida){
	//TODO: Actualizar la memoria compartida con los cambios puestos en partida ingresada por parametro.
	return;
};

/* Refresca la información de la partida con el ranking que actualiza el servidor torneo en la memoria compartida */
void Partida::refreshStatsJugadores(bool puntaje, bool ranking){
	/* Si pudo actualizar los jugadores desde la memoria compartida, notifico la actualizacion a los jugadores, sino pincha. */
	if(actualizarEstadoJugadores())
		notificarActEstadoJugadores(puntaje, ranking);
	else{
		cout << "[ERROR] - No se puede acceder a la memoria compartida, cerrando servidor partida." << endl;
		exit(0);
	}
};

/* */
bool Partida::actualizarEstadoJugadores(){
	t_partida *partidaShm = instance->getPartidaCompartida();

	/* Si no se puede obtener el puntero a la shm pincho porque algo funciono mal. */
	if(partidaShm != NULL){
		/* Actualizo los datos de los jugadores locales desde la memoria compartida */
		instance->partida.activo   = partidaShm->activo;
		instance->partida.jugador1 = partidaShm->jugador1;
		instance->partida.jugador2 = partidaShm->jugador2;
	}else
		return CONST_FALSE;

	return CONST_TRUE;
};

//---------------------------------------------Acciones----------------------------------------------------//

//Metodo para la accion ACC_MOVER, y mover el objeto con id:id y en el sentido sentido que la accion lo indique.
int Partida::move(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_CERO;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	//Verifico que se pueda mover la entidad a donde quiere moverse.
	if(sePuedeMover(action))
		instance->mover(action);

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_UNO;
};

/* Verifica si no existen marquesinas o ventanas abiertas para felix. */
bool Partida::sePuedeMover(t_accion *action){
	unsigned short int vidrioDestino = 0;

	//Me posiciono en la entidad seleccionada.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, action->id_entidad);

	//Si la entidad es felix, calculo el vidrio de destino.
	if((*it)->getTipo() == TIPO_ELEM_FELIX)
		vidrioDestino = ((Felix *)(*it))->getPosVidrio() + ((action->mov_x/ANCHO_MOV_X_FELIX) * ANCHO_MOV_VID_X_FEL)
														 + ((action->mov_y/ANCHO_MOV_Y_FELIX) * ANCHO_MOV_VID_Y_FEL);

	if((*it)->puedoMovermeHorizontalmente(action->mov_x) &&
	   (*it)->puedoMovermeVerticalmente(action->mov_y)   &&
	   !( action->id_entidad == CONST_ID_FELIX1 && estaFelixEnVidrio(CONST_DOS, vidrioDestino)) &&
	   !( action->id_entidad == CONST_ID_FELIX2 && estaFelixEnVidrio(CONST_UNO, vidrioDestino)) )
		return CONST_TRUE;

	return CONST_FALSE;

};

/* Metodo para realizar el movimiento propiamente dicho */
void Partida::mover(t_accion *action){
	//Me posiciono en la entidad seleccionada.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, action->id_entidad);

	//Muevo la entidad
	(*it)->moverse(action->mov_x, action->mov_y);
	//Devuelvo el movimiento al jugador para que lo ejecute.
	instance->encolarRespuestaJugadores(*action);

	//Si hay colisiones con el movimiento y alguno de los involucrados es uno de los felix proceso la colision, sino la ignoro.
	Elemento * colisionado = hayColision((*it));
	if(colisionado && (colisionado->getTipo()== TIPO_ELEM_FELIX || (*it)->getTipo()==TIPO_ELEM_FELIX )){
		procesarColision((*it), colisionado);
	}
};

/* Metodo para eliminar un ave */
bool Partida::deleteAve(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	/* Traigo el ave para ver si esta vivo, solo en ese caso lo borro. */
	Ave *ave = (Ave *)getElementoById(action->id_entidad);

	/* Si el ave esta viva la borro, sino no hago nada */
	if(ave->isVivo()){
		/* Le cambio el estado a no vivo */
		ave->setVivo(CONST_FALSE);

		/* Seteo el id del ave a nivel partida */
		this->idAve = CONST_MENOS_UNO;

		/* Encolo las acciones para devolversela al jugador y que las impacte. */
		t_accion action;

		/* Envio la accion delete. */
		action.id_accion  = ACC_DELETE_AVE;
		action.id_entidad = ave->getId();
		instance->encolarRespuestaJugadores(action);
	}

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_TRUE;
};

/* Metodo para eliminar una torta */
bool Partida::deleteTorta(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	/* Traigo la torta para ver si esta vivo, solo en ese caso lo borro. */
	Torta *torta = (Torta *)getElementoById(action->id_entidad);

	/* Si el ave esta viva la borro, sino no hago nada */
	if(torta->isVivo()){
		/* Le cambio el estado a no vivo */
		torta->setVivo(CONST_FALSE);

		/* Seteo el id de la torta a nivel partida */
		this->idTorta = CONST_MENOS_UNO;

		/* Encolo las acciones para devolversela al jugador y que las impacte. */
		t_accion action;

		/* Envio la accion delete. */
		action.id_accion  = ACC_DELETE_TORTA;
		action.id_entidad = torta->getId();
		instance->encolarRespuestaJugadores(action);
	}

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_TRUE;
}

/* Metodo para eliminar un ladrillo */
bool Partida::deleteLadrillo(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	/* Traigo el ladrillo para ver si esta vivo, solo en ese caso lo borro. */
	Ladrillo *lad = (Ladrillo *)getElementoById(action->id_entidad);

	/* Si el ladrillo esta vivo la borro, sino no hago nada */
	if(lad->isVivo()){
		/* Le cambio el estado a no vivo */
		lad->setVivo(CONST_FALSE);

		/* Cuento un ladrillo menos */
		instance->cantLadrillos--;

		/* Encolo las acciones para devolversela al jugador y que las impacte. */
		t_accion action;

		/* Envio la accion delete. */
		action.id_accion  = ACC_DELETE_LADRILLO;
		action.id_entidad = lad->getId();
		instance->encolarRespuestaJugadores(action);
	}

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_TRUE;
};

/* Trae el puntero al elemento del id ingresado, de la lista de elementos de la partida */
Elemento *Partida::getElementoById(int id){
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, id);
	return (*it);
};

/* Metodo de reparacion de una ventana. */
bool Partida::reparar(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	/* Traigo los elementos involucradas, siempre que se repara un vidrio se hace lo mismo al de arriba */
	Felix  *felix 	= (Felix *)getElementoById(action->id_entidad);
	Vidrio *vidrio1	= (Vidrio *)getElementoById(felix->getPosVidrio());
	Vidrio *vidrio2	= (Vidrio *)getElementoById(vidrio1->getId() + (ANCHO_MOV_VID_Y_FEL/CONST_DOS));

	/* Si el vidrio a reparar esta roto proceso la accion, sino no */
	if(vidrio1->estaRoto()){
		/* Le doy puntos a felix por reparar y cambio el estado de los vidrios */
		felix->reparar();
		vidrio1->reparar();
		vidrio2->reparar();

		/* Actualizo la cantidad de vidrios rotos, restandole los dos vidrios de la ventana */
		instance->cantVidriosRotos -= CONST_DOS;

		/* Notifico a los jugadores la reparacion */
		notificarReparacionVidrios(felix, vidrio1, vidrio2);

		/* Lo actualizo tambien en el servidor torneo */
		actualizarJugadorServidorTorneo(felix);

		/* Se fija si con esta reparacion ya termino el tramo y/o el nivel. */
		verFinTramoONivel();
	}

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_TRUE;
};

void Partida::verFinTramoONivel(){
	/* Si no quedan mas vidrios para reparar  termina el tramo */
	if(cantVidriosRotos == CONST_CERO){
		/* Primero paro la partida hasta que comience el nuevo tramo y/o nivel */
		instance->setStoped(CONST_TRUE);
		/* Despues encolo la accion para terminar el tramo o el nivel si es el ultimo tramo del nivel. */
		crearAccionFinTramoONivel();
	}
};

/* Notifica a los jugadores si ya se termino el tramo y o el nivel, con las acciones correspondientes */
void Partida::crearAccionFinTramoONivel(){
	/* Si el que termino era el ultimo tramo del nivel, encolo la accion de fin de nivel. */
	if(instance->tramoActual == instance->cantTramosNivelActual){
		t_accion actFinNivel;
		actFinNivel.id_accion  = ACC_FIN_NIVEL;
		actFinNivel.id_entidad = instance->nivelActual;
		instance->encolarAccionAProcesar(actFinNivel);
	}else{
		/* Si no termino el nivel, encolo el comando de fin de tramo. */
		t_accion actFinTramo;
		actFinTramo.id_accion  = ACC_FIN_TRAMO;
		actFinTramo.id_entidad = instance->tramoActual;
		instance->encolarAccionAProcesar(actFinTramo);
	}
}

/* Pasa al siguiente tramo */
bool Partida::finalizarTramo(t_accion *action){
	/* Mando a inicializar el siguiente tramo de la partida con el estado de los objetos y demas */
	instance->inicializarTramo((instance->tramoActual + 1));

	return CONST_TRUE;
};

/* Pasa al siguiente nivel. */
bool Partida::finalizarNivel(t_accion *action){
	/* Mando a inicializar el siguiente nivel de la partida */
	instance->inicializarNivel(instance->nivelActual + 1);

	return CONST_TRUE;
};

/* Termina la partida. */
bool Partida::finalizarPartida(t_accion *action){
	//TODO: Ver si faltan acciones

	/*Paro la partida para dejar de tomar acciones de los jugadores */
	instance->stoped = CONST_TRUE;

	/* Notifico a los jugadores del fin de la partida */
	notificarFinPartida();

	/* Comienzo desactivando la partida */
	instance->activo = CONST_FALSE;

	/* Interrumpo al proceso llamador para que sepa que termino la partida */
	kill(SIGUSR1, getpid());
	//TODO: Ver esto si se puede hacer o no.

	return CONST_TRUE;
};

/*
 * Método que realiza la animación de los objetos controlados por el programa
 * como ralph, ladrillos, aves.
 */
void Partida::animar(){
	this->mutexListaElementos->P();

	instance->eventoAnimarAve();
	instance->eventoAnimarTorta();
	instance->eventoAnimarRalph();
	instance->eventoAnimarLadrillos();

	this->mutexListaElementos->V();
};

/*
 *Evento que anima a ralph en el techo.
 */
void Partida::eventoAnimarRalph(){
	if(instance->idRalph != CONST_MENOS_UNO){
	    t_accion action;

		//Me posiciono en ralph en la lista de objetos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, instance->idRalph);
		//Verifico que sea elemento del tipo ralph
		if((*it)->getTipo() == TIPO_ELEM_RALPH && (*it)->isVivo()){

			//Si no puede realizar el movimiento invierto el sentido. (de right to left y viceversa)
			if(!((*it)->puedoMovermeHorizontalmente(ANCHO_MOV_X_RALPH)))
				instance->sentidoRalph = instance->sentidoRalph * CONST_MENOS_UNO;

			//Si encontre a ralph encolo la t_accion de moverse.
	        action.id_accion = ACC_MOVER;
	        action.id_entidad= (*it)->getId();
	        action.mov_x 	 = instance->sentidoRalph * ANCHO_MOV_X_RALPH;
	        action.mov_y 	 = CONST_CERO;

		    //Encolo el movimiento de ralph
			instance->encolarAccionAProcesar(action);

			//Veo si ya paso la cantidad de movimientos sin lanzar
			Ralph *ralph = (Ralph *) (*it);
			if(ralph->getCantMovSinLanzar() == instance->maxMovSinLanzar){
				//De ser así lanzo el ladrillo e inicializo el contador.
				instance->crearAccionLanzarLadrillo(ralph);
			}else{
				//Sino cuento un movimiento mas.
				ralph->addMovSinLanzar();
			}
		}
	} else
		cout << "[ERROR] - Se intenta animar a ralph pero aún no se creo. " << endl;
};

/*
 *Evento que anima los ladrillos que existen en la partida.
 */
void Partida::eventoAnimarLadrillos(){ 	
 	//Si no ningun ladrillo no los animo.
    if(instance->cantLadrillos != CONST_CERO) {
	    bool desaparecer = false;
	    t_accion action;

		//Busco el objeto en la list de objetos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, ID_BASE_LADRILLO);
		for (; it != listaElementos.end(); ++it){
			//Por cada ladrillo que encuentro.
    		if((*it)->getTipo() == TIPO_ELEM_LADRILLO && (*it)->isVivo()){
    			//Si no puede realizar el movimiento la desaparezco de la pantalla
    			if(!(*it)->puedoMovermeVerticalmente(ANCHO_MOV_Y_OBJ))
    				desaparecer = true;	    		

		    	//Si hay que desaparecer, encolo la t_accion correspondiente
		        if(desaparecer){
		            action.id_accion = ACC_DELETE_LADRILLO;
		            action.id_entidad= (*it)->getId();
		        }else {
		        	//Sino encolo la t_accion de moverse.
		            action.id_accion = ACC_MOVER;
		            action.id_entidad= (*it)->getId();
		            action.mov_x 	 = CONST_CERO;
		            action.mov_y 	 = ANCHO_MOV_Y_OBJ;
		        }

		        //Por cada ladrillo encolo la t_accion de moverlo.
		    	instance->encolarAccionAProcesar(action);
    		}
    	}
	}
};

/*
 *Evento que anima las aves que existen en la partida.
 */
void Partida::eventoAnimarAve(){
	//Comienzo viendo si ya existe un ave.
	if(instance->idAve != CONST_MENOS_UNO){
		bool desaparecer = false;
    	t_accion action;

		//Me posiciono en el ave en la lista de objetos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, instance->idAve);
		//Verifico que sea un elemento del tipo ave y este vivo
		if((*it)->getTipo() == TIPO_ELEM_AVE && (*it)->isVivo()){
			//Si no puede realizar el movimiento la desaparezco de la pantalla
			if(!(*it)->puedoMovermeHorizontalmente(ANCHO_MOV_X_OBJ))
				desaparecer = true;	

	    	//Si hay que desaparecer, encolo la t_accion correspondiente
	        if(desaparecer){
	            action.id_accion = ACC_DELETE_AVE;
	            action.id_entidad= (*it)->getId();
	        }else {
	        	//Sino encolo la t_accion de moverse.
	            action.id_accion = ACC_MOVER;
	            action.id_entidad= (*it)->getId();
	            action.mov_x 	 = ANCHO_MOV_X_OBJ;
	            action.mov_y 	 = CONST_CERO;
	        }
	        //Encolo el movimiento o desaparicion.
	    	instance->encolarAccionAProcesar(action);
    	}else{
    		cout << "[ERROR] - Se asigno mal el id del ave, el objeto no es del tipo AVE" << endl;
    	}
	}else{
		//Si no existe un ave aun, veo si ya pasaron el maximo de ciclos sin aves, entonces la creo.
		if(instance->cantCiclosSinAves == instance->maxCiclosSinAves){
			instance->crearAccionAve();
		}else{
			//Sino cuento un ciclo mas sin aves.
			instance->cantCiclosSinAves++;
		}
	}
};


/*
 *Evento que anima las tortas que existen en la partida.
 */
void Partida::eventoAnimarTorta(){
	/* Comienzo viendo si ya existe una torta */
	if(instance->idTorta == CONST_MENOS_UNO){
		/* Si no existe una torta aun, veo si ya pasaron el maximo de ciclos sin tortas, entonces la creo. */
		if(instance->cantCiclosSinTortas == instance->maxCiclosSinTortas){
			instance->crearAccionTorta();
		}else{
			/* Sino cuento un ciclo mas sin aves. */
			instance->cantCiclosSinTortas++;
		}
	}
};

/*
 * Hace que ralph recibido por parámetro, lance un ladrillo.
 * Este método se divide en dos acciones, una sería crear el ladrillo en la pos x e y, donde termina ralph
 * y la otra es que ralph haga el movimiento de lanzar ladrillo.
 * Ambas se enconlan para ser procesadas por el consumidor.
 */
void Partida::crearAccionLanzarLadrillo(Ralph *ralph){
	//Solo voy a lanzar ladrillos, cuando halla menos de 5 en pantalla, es el maximo.
	if(instance->cantLadrillos < MAX_CANT_LADRILLOS){
		t_accion accionLanzar;
		t_accion accionCrear;

		//Primero mando a ralph a hacer los movimientos de lanzar.
		accionLanzar.id_accion = ACC_LANZAR_LADRILLO;
		accionLanzar.id_entidad= ralph->getId();
		instance->encolarAccionAProcesar(accionLanzar);

		//Despues mando a crear el ladrillo a los pies de ralph, en la mitad del ancho.
		accionCrear.id_accion = ACC_CREAR_LADRILLO;
		accionCrear.mov_x 	  = ralph->getPosX() + (ralph->getAncho()/2);	//Arranca justo en la mitad del cuerpo de ralph.
		accionCrear.mov_y 	  = ralph->getPosY() + ralph->getAlto();		//Arranca en los pies de ralph.
		instance->encolarAccionAProcesar(accionCrear);
	}
};

/*
 * Encola el comando de crear un ave, cuando no existe y ya se cumplio el tiempo de espera.
 * la posición (x,y) del ave, se definen aleatoriamente.
 */
void Partida::crearAccionAve(){
	t_accion accionCrear;

	//Defino la posición inicial del ave, a elegir entre los tres estamentos de ventana existentes.
    srand(time(NULL));
    int x = CONST_CERO;
    int y = POS_INI_Y_AVE - ((rand() % CONST_TRES) * ANCHO_MOV_Y_AVE);

	//Despues envio el comando para crear el ave en el x,y seleccionados.
	accionCrear.id_accion	= ACC_CREAR_AVE;
	accionCrear.id_entidad	= CONST_ID_AVE;
	accionCrear.mov_x 		= x;
	accionCrear.mov_y 		= y;
   	instance->encolarAccionAProcesar(accionCrear);
};


/*
 * Encola el comando de crear una torta, cuando no existe y ya se cumplio el tiempo de espera.
 * El vidrio de la torta, se definen aleatoriamente.
 */
void Partida::crearAccionTorta(){
	t_accion accionCrear;

	//Defino la posición inicial de la torta, elige en cual de las 15 ventanas va a estar.
    srand(time(NULL));
    int posVidrio = ((rand() % CONST_CANT_VENTANAS) * ANCHO_MOV_VID_X_FEL);

	//Despues envio el comando para crear el ave en el x,y seleccionados.
	accionCrear.id_accion	= ACC_CREAR_TORTA;
	accionCrear.id_entidad	= CONST_ID_TORTA;
	accionCrear.mov_x 		= posVidrio;
   	instance->encolarAccionAProcesar(accionCrear);
};

//Crea los elementos iniciales vacios sin vida.
void Partida::crearElementosIniciales(){
	//Tomo el mutex de la lista de elementos a modificar.
	this->mutexListaElementos->P();

	//Creo los objetos iniciales necesarios para la partida.
	//Vidrios
	crearVidriosIniciales();
	//Bloqueos
	crearBloqueosIniciales();
	//Ralph
	crearRalph();
	//Felix1
	crearFelix((unsigned char)CONST_UNO);
	//Felix2
	crearFelix((unsigned char)CONST_DOS);
	//Ave inicial no viva.
	crearAveInicial();
	//Torta inicial no viva.
	crearTortaInicial();
	//Stock de ladrillos iniciales no vivos.
	crearLadrillosIniciales();

	//La libero
	this->mutexListaElementos->V();
};

//Sorte el estado de los vidrios y bloqueos para el tramo en cuestión.
void Partida::sortearEstadosTramo(int semilla){
	//TODO:
	//Tomo el mutex de la lista de elementos a modificar
	this->mutexListaElementos->P();

	//Vidrios
	sortearVidriosRotos();
	//Bloqueos
	sortearBloqueosActivos();

	//Libero
	this->mutexListaElementos->V();
};

//Crea todos los objetos vidrios con estado no vivo y no roto.
void Partida::crearVidriosIniciales(){
	//Comienzo generando los objetos vidrios y sorteando el estado de roto o no.
	for(int i=MIN_IDX_VIDRIOS; i<=MAX_IDX_VIDRIOS; i++){
		unsigned char estado = (unsigned char)(rand()% CANT_ESTADOS_VIDRIO);
		Vidrio *vx = new Vidrio(i,  CONST_CERO, CONST_CERO, CONST_CERO, CONST_CERO, CONST_CERO, TIPO_ELEM_VIDRIO, CONST_TRUE,estado);
		instance->listaElementos.push_back((Elemento *)vx);
	}
};

void Partida::crearBloqueosIniciales(){
	unsigned short int vidrioOrigen;
	unsigned short int vidrioDestino;

	//Ahora creo los bloqueos uno a uno
	for(int i=MIN_IDX_BLOQUEOS; i<=MAX_IDX_BLOQUEOS; i++){
		//Defino los vidrios de origen y destino
		definirOrigenYDestino(i, &vidrioOrigen, &vidrioDestino);

		//Instancio el bloque con el estado sorteado y lo inserto en la lista al final.
		Bloqueo *bq = new Bloqueo(i, CONST_CERO, CONST_CERO, CONST_CERO, CONST_CERO, CONST_CERO, TIPO_ELEM_BLOQUEO, CONST_FALSE, ESTADO_BLOQ_FREE, vidrioOrigen, vidrioDestino);
		instance->listaElementos.push_back((Elemento *)bq);
	}
};

//Crea todos los objetos vidrios con estado no vivo y no roto.
void Partida::sortearVidriosRotos(){
	//Comienzo recorriendo todos los vidrios creados
	for(int idVidrio=MIN_IDX_VIDRIOS; idVidrio<=MAX_IDX_VIDRIOS; idVidrio++){
		/* Sorteo el estado. */
		unsigned char estado = (unsigned char)(rand()% CANT_ESTADOS_VIDRIO);

		/* Me posiciono en el vidrio seleccionado sobre la lista de elementos. */
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, idVidrio);

		/* Modifico el estado */
		((Vidrio *)(*it))->setEstado(estado);

		/* Si sale sorteado roto, cuento un vidrio roto mas. */
		if(estado == ESTADO_VIDRIO_ROTO)
			cantVidriosRotos++;
	}
};

//Sortea que bloqueos tendrá este tramo, a partir de la semilla generada en la funcion llamadora.
void Partida::sortearBloqueosActivos(){
	unsigned char estado;
	bool vivo = CONST_FALSE;

	//Defino el tipo de bloqueo a usar en este tramo, si por fila o por columna.
	unsigned char tipoBloqueo = (unsigned char)(rand()% CANT_TIPOS_BLOQ);

	//Ahora creo los bloqueos uno a uno
	for(int idBloqueo=MIN_IDX_BLOQUEOS; idBloqueo<=MAX_IDX_BLOQUEOS; idBloqueo++){
		//Sorteo el estado del bloqueo en proceso.
		estado = sortearEstadoBloqueo(idBloqueo, tipoBloqueo);

		//Si esta bloqueado es porque es un bloqueo activo.
		if(estado == ESTADO_BLOQ_LOCK)
			vivo = CONST_TRUE;
		else
			vivo = CONST_FALSE;

		//Me posiciono en el bloqueo seleccionado sobre la lista de elementos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, idBloqueo);

		//Modifico el estado
		((Bloqueo *)(*it))->setEstado(ESTADO_BLOQ_LOCK);
		((Bloqueo *)(*it))->setVivo(vivo);
	}

	/*Ahora quito n bloqueos por fila o columna dependiendo del tipoBloqueo para que sea posible terminar el tramo.
	  defino la cantidad de bloqueos a quitar dependiendo del tipo de bloqueo, si es horizontal o vertical */
	if(tipoBloqueo == TIPO_BLOQ_HORIZ)
		quitarBloqueosParaJugar(this->cantBloqueosAQuitarH, tipoBloqueo);
	else
		quitarBloqueosParaJugar(this->cantBloqueosAQuitarV, tipoBloqueo);

};

//Quita aleatoriamente la cantidad de bloqueos por fila o columna ingresada por parámetro.
void Partida::quitarBloqueosParaJugar(unsigned short int cantBloqueosAQuitar, unsigned char tipoBloqueo){
	//Voy a eliminar de a uno, la cantidad de bloqueos ingresada por parametro.
	for(int x=CONST_CERO; x < cantBloqueosAQuitar; x++){
		//Me fijo si son bloqueos verticales u horizontales.
		if(tipoBloqueo == TIPO_BLOQ_VERTI)
			instance->quitarBloqueoVertical(tipoBloqueo);
		else
			instance->quitarBloqueoHorizontal(tipoBloqueo);
	}
};

/**
 * Quita un bloqueo horizontal al azar por cada fila.
 */
void Partida::quitarBloqueoHorizontal(unsigned char tipoBloqueo){
	//Por cada fila en la pantalla.
	for(unsigned short int fila=CONST_CERO; fila < CANT_FIL_VENTANAS; fila++){
		//Defino el idBase a usar para luego offsetear por columna dentro de la fila.
		int idBase = instance->getIdBaseBloqueo(tipoBloqueo, fila);

		//Sorteo cual de los bloqueos por fila voy a liberar, si de columna 1 o columna 2 o columna 3 o columna 4 o columna 5
		unsigned short int columnaALiberar = (rand() % CANT_BLOQ_X_FIL);

		//Genero el id_entidad del bloqueo offseteando del minimo id para esta fila.
		int idBloqueo = idBase + columnaALiberar;

		//Me posiciono en el bloqueo seleccionado sobre la lista de elementos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, idBloqueo);

		//Modifico el estado
		((Bloqueo *)(*it))->setEstado(ESTADO_BLOQ_FREE);
	}
}

/* Quita un bloqueo vertical al azar por cada columna. */
void Partida::quitarBloqueoVertical(unsigned char tipoBloqueo){
	//Por cada columna en la pantalla.
	for(unsigned short int col=CONST_CERO; col< CANT_COL_VENTANAS; col++){

		//Sorteo cual de los dos bloqueos por columna voy a liberar, si de fila 1 o fila 2
		unsigned short int filaALiberar = (rand() % CANT_BLOQ_X_COL);

		//Defino el idBase del cual offsetear por fila, para saber que bloqueo liberar.
		unsigned short int idBase = getIdBaseBloqueo(tipoBloqueo, filaALiberar);

		//Genero el id_entidad del bloqueo offseteando del minimo id para esta fila por offset que representa un salto de fila..
		int idBloqueo = idBase + col;

		//Me posiciono en el bloqueo seleccionado sobre la lista de elementos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, idBloqueo);

		//Modifico el estado
		((Bloqueo *)(*it))->setEstado(ESTADO_BLOQ_FREE);
	}
}

//Trae los idEntidad base para offsetear y obtener los id de los bloqueos buscados.
unsigned short int Partida::getIdBaseBloqueo(unsigned char tipoBloqueo, int fila){
	//Veo si es bloqueo horizontal o vertical.
	if(tipoBloqueo == TIPO_BLOQ_HORIZ)
		switch(fila){
			case CONST_CERO:
				return MIN_IDX_BLOQH_FILA1;
				break;
			case CONST_UNO:
				return MIN_IDX_BLOQH_FILA2;
				break;
			case CONST_DOS:
				return MIN_IDX_BLOQH_FILA3;
				break;
		}
	else
		switch(fila){
			case CONST_CERO:
				return MIN_IDX_BLOQV_FILA1;
				break;
			case CONST_UNO:
				return MIN_IDX_BLOQV_FILA2;
				break;
		}
	return CONST_CERO;
};

//Define los vidrios de origen y destino para el bloqueo con indice = i;
void Partida::definirOrigenYDestino(int i, unsigned short int *vidrioOrigen, unsigned short int *vidrioDestino){
	//Defino el numero de bloqueo, offseteando al indice del bloque cero 0.
	int nroBloqueo = i - MIN_IDX_BLOQUEOS;

	//Bloqueos horizontales de la primera fila
	if(i >= MIN_IDX_BLOQH_FILA1 && i <= MAX_IDX_BLOQH_FILA1){
		*vidrioOrigen  = nroBloqueo + CONST_CERO;
		*vidrioDestino = nroBloqueo + CONST_UNO;
	};
	//Bloqueos verticales de la primera fila
	if(i >= MIN_IDX_BLOQV_FILA1 && i <= MAX_IDX_BLOQV_FILA1){
		*vidrioOrigen  = nroBloqueo - CONST_CUATRO;
		*vidrioDestino = nroBloqueo + CONST_SEIS;
	};
	//Bloqueos horizontales de la segunda fila
	if(i >= MIN_IDX_BLOQH_FILA2 && i <= MAX_IDX_BLOQH_FILA2){
		*vidrioOrigen  = nroBloqueo + CONST_UNO;
		*vidrioDestino = nroBloqueo + CONST_DOS;
	};
	//Bloqueos verticales de la segunda fila
	if(i >= MIN_IDX_BLOQV_FILA2 && i <= MAX_IDX_BLOQV_FILA2){
		*vidrioOrigen  = nroBloqueo - CONST_TRES;
		*vidrioDestino = nroBloqueo + CONST_SIETE;
	};
	//Bloqueos horizontales de la tercera fila
	if(i >= MIN_IDX_BLOQH_FILA3 && i <= MAX_IDX_BLOQH_FILA3){
		*vidrioOrigen  = nroBloqueo + CONST_DOS;
		*vidrioDestino = nroBloqueo + CONST_TRES;
	};
};

//Sortea el estado para cada bloqueo del tramo, uno a la vez.
unsigned char Partida::sortearEstadoBloqueo(int i, unsigned char tipoBloqueo){
	//Tomo por defecto el estado libre.
	unsigned char estado = ESTADO_BLOQ_FREE;

	//Dependiendo del tipo de bloqueo aleatorio seleccionado veo si activo o no este bloqueo.
	if(tipoBloqueo == TIPO_BLOQ_VERTI){
		//Bloqueos por columna, solo bloquea los bloqueos verticales, identificados por el indice.
		if((i >= MIN_IDX_BLOQV_FILA1 && i<=MAX_IDX_BLOQV_FILA1) ||
		   (i >= MIN_IDX_BLOQV_FILA2 && i<=MAX_IDX_BLOQV_FILA2)   ){
			estado = ESTADO_BLOQ_LOCK;
		}
	}else{
		//Bloqueos por fila
		if((i >= MIN_IDX_BLOQH_FILA1 && i<=MAX_IDX_BLOQH_FILA1) ||
		   (i >= MIN_IDX_BLOQH_FILA2 && i<=MAX_IDX_BLOQH_FILA2) ||
		   (i >= MIN_IDX_BLOQH_FILA3 && i<=MAX_IDX_BLOQH_FILA3)   ){
			estado = ESTADO_BLOQ_LOCK;
		}
	};

	return estado;
};

//Accion que crea un ave.
void Partida::crearFelix(unsigned char nroFelix){
	unsigned short int id;
	unsigned short int posVidrio;
	int posX;
	int posY;
	long int puntos;

	//Veo si estoy creando al felix1 o al felix2.
	if(nroFelix == CONST_UNO){
		id 		  = CONST_ID_FELIX1;
		posX	  = POS_INI_X_FELIX1;
		posY	  = POS_INI_Y_FELIX1;
		posVidrio = POS_INI_VID_FELIX1;
		puntos	  = instance->getPartida()->jugador1.puntaje;
	}else{
		id 		  = CONST_ID_FELIX2;
		posX	  = POS_INI_X_FELIX2;
		posY	  = POS_INI_Y_FELIX2;
		posVidrio = POS_INI_VID_FELIX2;
		puntos	  = instance->getPartida()->jugador2.puntaje;
	}

	//Creo la instancia del felix correspondiente y lo inserto.
	Felix *felix = new Felix(id, CONST_CERO, posX, posY, ALTO_FELIX, ANCHO_FELIX, TIPO_ELEM_FELIX, CONST_TRUE, VIDAS_POR_DEFECTO, posVidrio, puntos);
	instance->listaElementos.push_back((Elemento *)felix);
};

//Accion que crea un ave.
void Partida::crearRalph(){
	//Creo Ralph en la posición inicial por defecto y seteo el idRalph en el singleton para accederlo mas rápido.
	Ralph *ralph = new Ralph(CONST_ID_RALPH, CONST_CERO, POS_INI_X_RALPH, POS_INI_Y_RALPH, ALTO_RALPH,ANCHO_RALPH, TIPO_ELEM_RALPH, CONST_TRUE);
	instance->listaElementos.push_back((Elemento *)ralph);

	//Seteo el id de ralph para no buscarlo y acceder directamente.
	this->idRalph = CONST_ID_RALPH;
};

//Crea el elemento ave inicial, pero en estado no vivo, cuando quiere aparecer un ave pasa a vivo = true;
void Partida::crearAveInicial(){
	//Creo el Ave en el indice por defecto 55 y con vivo = false, para que se active recien cuando se anima por el juego mismo.
	Ave *ave = new Ave(CONST_ID_AVE, CONST_CERO, CONST_CERO, CONST_CERO, ALTO_AVE,
					   ANCHO_AVE, TIPO_ELEM_AVE, CONST_FALSE);
	instance->listaElementos.push_back((Elemento *)ave);
};

void Partida::crearTortaInicial(){
	//Creo el Ave en el indice por defecto 56 y con vivo = false, para que se active recien cuando se anima por el juego mismo.
	Torta * torta = new Torta(CONST_ID_TORTA, CONST_CERO, CONST_CERO, CONST_CERO, ALTO_TORTA,
							  ANCHO_TORTA, TIPO_ELEM_TORTA, CONST_FALSE, CONST_CERO);
	instance->listaElementos.push_back((Elemento *)torta);
};

void Partida::crearLadrillosIniciales(){
	//Creo el stock de ladrillos no vivos, para que se vallan activando a medida que sean lanzados.
	for(int index=CONST_CERO; index<MAX_CANT_LADRILLOS; index++){
		//Calculo el id del ladrillo, tomando el id base que parte del ladrillo 0 y sumandole el indice del ciclo de creación.
		unsigned short int idLadrillo = ID_BASE_LADRILLO + index;
		//Lo creo y lo inserto en la lista de elementos.
		Ladrillo *l = new Ladrillo(idLadrillo, CONST_CERO, CONST_CERO, CONST_CERO, ALTO_LADRILLO,
								   ANCHO_LADRILLO, TIPO_ELEM_LADRILLO, CONST_FALSE);
		instance->listaElementos.push_back((Elemento *)l);
	}
};

//--------------------------------------------------------------------------------------------------------//

//												Getters													  //
t_partida *Partida::getPartida(){
	return &(this->partida);
};

bool Partida::isStoped(){
	return instance->stoped;
}

bool Partida::isActivo(){
	return activo;
}

//--------------------------------------------------------------------------------------------------------//
bool Partida::estaFelixEnVidrio(unsigned short int nroFelix, unsigned short int vidrioDestino){
	int idFelix;

	if(nroFelix == 1)
		idFelix = CONST_ID_FELIX1;
	else
		idFelix = CONST_ID_FELIX2;

	//Me posiciono en el bloqueo seleccionado sobre la lista de elementos.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, idFelix);

	return (((Felix *) (*it))->getPosVidrio() == vidrioDestino);
}

/* Verifica si el elemento recien movido, no colisiono contra algo, si es asi devuelve contra que. */
Elemento *Partida::hayColision(Elemento *enMovimiento){

	//Recorro la lista de objetos viendo si colisiona con alguno de los vivos. solo veo aves, ladrillos o tortas, el resto no.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, CONST_ID_AVE);
	for (; it != listaElementos.end(); ++it){
		//La comparacion tiene que ser contra todos, menos contra si mismo.
		if((*it)->getId() != enMovimiento->getId() && (*it)->isVivo())
		{
			Elemento *elem = (*it);
			if(	( (enMovimiento->getPosX() > elem ->getPosX()) &&
				  (enMovimiento->getPosX() < (elem ->getPosX() + elem ->getAncho())) &&
				  (enMovimiento->getPosY() > elem ->getPosY()) &&
				  (enMovimiento->getPosY() < (elem ->getPosY() + elem ->getAlto())) )
				 ||
				( ((enMovimiento->getPosX() + elem ->getAncho()) > elem ->getPosX()) &&
				  ((enMovimiento->getPosX() + elem ->getAncho()) < (elem ->getPosX() + elem ->getAncho())) &&
				  ((enMovimiento->getPosY() + elem ->getAlto()) > elem ->getPosY()) &&
				  ((enMovimiento->getPosY() + elem ->getAlto()) < (elem ->getPosY() + elem ->getAlto())) )		)
				return elem;
			else{ //Veo si no es que se cruzan en un vidrio una torta y un felix.
				if(elem->getTipo() == TIPO_ELEM_TORTA && enMovimiento->getTipo() == TIPO_ELEM_FELIX ){
					if(((Torta *)elem)->getPosVidrio() == ((Felix *) enMovimiento)->getPosVidrio())
						return elem;
				}
			}
		}
	}

	return NULL;
}

/*
 * Realiza el impacto necesario sobre los felix cuando son colisionados.
 */
void Partida::procesarColision(Elemento *colisionador, Elemento *colisionado){
	Felix	 *felix    = NULL;
	Elemento *elemento = NULL;

	//Me fijo cual de los dos es felix, porque es quien se ve impactado el otro elemnto sigue como si nada.
	if(colisionador->getTipo()==TIPO_ELEM_FELIX){
		felix 	 = (Felix *)colisionador;
		elemento = colisionado;
	}else{
		felix 	 = (Felix *)colisionado;
		elemento = colisionador;
	}
	//Si no es inmune lo colisiono.
	if(!felix->isInmune()){
		colisionar(felix, elemento);
	}
};

/**
 * Ejecuta la colision de uno de los felix contra cualquier objeto.
 */
void Partida::colisionar(Felix *felix, Elemento *elemento){
	/* Si es una torta, la accion a ejecutar es volver inmune a felix y eliminar la torta. */
	if(elemento->getTipo()==TIPO_ELEM_TORTA){
		consumirTorta(felix, ((Torta *)elemento));
	}else{
		/* Sino Le resto la vida a felix */
		felix->restarVida();

		/* Encolo la accion para devolversela al jugador y que la impacte */
		t_accion action;
		action.id_accion  = ACC_RESTAR_VIDA;
		action.id_entidad = felix->getId();
		instance->encolarRespuestaJugadores(action);

		/* Si murio con esta colision, envio la accion al jugador de morir este felix */
		if(!felix->isVivo()){
			action.id_accion  = ACC_MORIR;
			action.id_entidad = felix->getId();
			instance->encolarRespuestaJugadores(action);
		}
	}
};

/*
 * Hace que felix consuma la torta, se vuelva inmune y desaparezca la torta de la pantalla.
 */
void Partida::consumirTorta(Felix *felix, Torta *torta){
	/* Vuelvo inmune a felix , le sumo los puntos de la torta y mato la torta*/
	felix->setInmune(CONST_TRUE);
	felix->sumarPuntos(PTOS_CONS_TORTA);
	torta->setVivo(CONST_FALSE);

	/* Encolo las acciones para devolversela al jugador y que las impacte
	 * se generan 3 distintas, inmunizar a felix, actualizar puntaje del jugador y desaparecer la torta.
	 */
	t_accion action;

	/* Inmunizar */
	action.id_accion  = ACC_CONSUMIR_TORTA;
	action.id_entidad = felix->getId();
	instance->encolarRespuestaJugadores(action);

	/* Actualizar puntaje */
	action.id_accion  = ACC_ACT_PUNT;
	action.id_entidad = felix->getId();
	action.mov_x	  = felix->getPuntos();
	instance->encolarRespuestaJugadores(action);
	actualizarJugadorServidorTorneo(felix);

	/* Eliminar torta */
	action.id_accion  = ACC_DELETE_TORTA;
	action.id_entidad = torta->getId();
	action.mov_x 	  = torta->getPosVidrio();
	instance->encolarRespuestaJugadores(action);
};

/* Metodo que interpreta la accion crear ave y activa un ave en la pos x,y indicada en la accion */
bool Partida::crearAve(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Traigo el ave de la lista de elementos */
	Ave *ave = (Ave *) getElementoById(action->id_entidad);

	/* Revivo el ave, cambiandole la posicion a donde me lo indica la accion */
	ave->setVivo(CONST_TRUE);
	ave->setPosX(action->mov_x);
	ave->setPosY(action->mov_y);

	/* Seteo el id del ave para que sepan que esta creada y que en este ciclo se creo un ave. */
	instance->idAve = CONST_ID_AVE;
	instance->cantCiclosSinAves = CONST_CERO;

	/* Envio la misma accion a los jugadores */
	t_accion a;
	a.id_accion	 = ACC_CREAR_AVE;
	a.id_entidad = ave->getId();
	a.mov_x		 = action->mov_x;
	a.mov_y		 = action->mov_y;
	encolarRespuestaJugadores(a);

	return CONST_TRUE;
};

/* Metodo que interpreta la accion crear ave y activa un ave en la pos x,y indicada en la accion */
bool Partida::crearTorta(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Traigo la torta de la lista de elementos */
	Torta *torta = (Torta *) getElementoById(action->id_entidad);

	/* Revivo la torta, cambiandole el vidrio a donde me lo indica la accion */
	torta->setVivo(CONST_TRUE);
	torta->setPosVidrio(action->mov_x);

	/* Seteo el id de la torta para que sepan que esta creada y que en este ciclo se creo una torta. */
	instance->idTorta = CONST_ID_TORTA;
	instance->cantCiclosSinTortas = CONST_CERO;

	/* Envio la misma accion a los jugadores */
	t_accion a;
	a.id_accion	 = ACC_CREAR_TORTA;
	a.id_entidad = torta->getId();
	a.mov_x		 = action->mov_x;
	a.mov_y		 = action->mov_y;
	encolarRespuestaJugadores(a);

	return CONST_TRUE;
};


/* Metodo que interpreta la accion crear ladrillo */
bool Partida::crearLadrillo(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Si todavia hay lugar para un ladrillo mas */
	if(instance->cantLadrillos < MAX_CANT_LADRILLOS){

		/* Busco el primer ladrillo de la lista que no este vivo,partiendo del Id base de los ladrillos. */
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, ID_BASE_LADRILLO);
		for (; it != listaElementos.end(); ++it){

			/* Por cada ladrillo que encuentro si no esta vivo, lo reutilizo. */
    		if((*it)->getTipo() == TIPO_ELEM_LADRILLO && !((*it)->isVivo()) ){

    			/* Revivo el ladrillo */
    			reutilizarLadrillo(((Ladrillo *)(*it)), action->mov_x, action->mov_y);

    			/* Envio la misma accion a los jugadores */
    			t_accion a;
    			a.id_accion	 = ACC_CREAR_LADRILLO;
    			a.id_entidad = (*it)->getId();
    			a.mov_x		 = action->mov_x;
    			a.mov_y		 = action->mov_y;
    			encolarRespuestaJugadores(a);

    			/* Salgo del ciclo */
    			break;
    		}
		}
	}

	return CONST_TRUE;
};

/* Revive un ladrilo y lo posiciona en los x,y ingresados por parametro. */
void Partida::reutilizarLadrillo(Ladrillo *ladrillo, int posX, int posY){
	/* Activo el ladrillo pasandolo a vivo cambiandole la posicion a donde me lo indica la accion */
	ladrillo->setVivo(CONST_TRUE);
	ladrillo->setPosX(posX);
	ladrillo->setPosY(posY);

	/* Incremento la cantidad de ladrillos en pantalla para evitar pasar el maximo. */
	instance->cantLadrillos++;
};

/* Hace que ralph se mueva como lanzando un ladrillo */
bool Partida::lanzarLadrillo(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Traigo la torta de la lista de elementos */
	Ralph *ralph = (Ralph *) getElementoById(action->id_entidad);

	/* Si esta vivo envio a los jugadores la orden de animar a ralph moviendo un ladrillo. */
	if(ralph->isVivo()){
		/* Seteo en cero la cantidad de movimientos sin lanzar ladrillos de ralph. */
		ralph->setCantMovSinLanzar(CONST_CERO);

		/* Envio la misma accion a los jugadores */
		t_accion a;
		a.id_accion	 = ACC_LANZAR_LADRILLO;
		a.id_entidad = ralph->getId();
		a.mov_x		 = action->mov_x;
		a.mov_y		 = action->mov_y;
		encolarRespuestaJugadores(a);
	}

	return CONST_TRUE;
};

/* Resetea todos los elementos que son persistentes dentro de la partida o nivel y no se crean en el tramo */
void Partida::resetObjetosPorTramo(){
	resetRalph();
	resetFelixes();
	resetLadrillos();
	resetAve();
	resetTorta();
};

/* Inicializa ralph a la posicion inicial del tramo y su cantidad de movimientos sin lanzar */
void Partida::resetRalph(){
	/* Busco a ralph dentro de la lista de elementos */
	Ralph *ralph = (Ralph *)getElementoById(CONST_ID_RALPH);

	/* Ahora lo ubico en las posiciones x,y de inicio de ralph y pongo en cero el contador para lanzar */
	ralph->setPosX(POS_INI_X_RALPH);
	ralph->setPosY(POS_INI_Y_RALPH);
	ralph->setCantMovSinLanzar(CONST_CERO);

	/* Piso el nombre por si no estaba */
	instance->idRalph = CONST_ID_RALPH;
}

/* Inicializa los felix1 y felix2 a la posicion inicial y los desinmuniza */
void Partida::resetFelixes(){
	/* Busco a felix 1 y felix 2*/
	Felix *felix1 = (Felix *)getElementoById(CONST_ID_FELIX1);
	Felix *felix2 = (Felix *)getElementoById(CONST_ID_FELIX2);

	/* Ahora lo ubico en las posiciones x,y y de vidrio de inicio de cada uno*/
	felix1->setPosX(POS_INI_X_FELIX1);
	felix1->setPosY(POS_INI_Y_FELIX1);
	felix1->setPosVidrio(POS_INI_VID_FELIX1);

	felix2->setPosX(POS_INI_X_FELIX2);
	felix2->setPosY(POS_INI_Y_FELIX2);
	felix2->setPosVidrio(POS_INI_VID_FELIX2);

	/* Los desinmunizo por si estaban inmunizados */
	felix1->setInmune(CONST_FALSE);
	felix2->setInmune(CONST_FALSE);
}

/* Elimina todos los ladrillos vivos para arrancar sin ladrillos el tramo */
void Partida::resetLadrillos(){
	/* Recorro todos los ladrillos */
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, ID_BASE_LADRILLO);
	for (; it != listaElementos.end(); ++it){
		/* Por cada ladrillo que encuentro. */
		if((*it)->getTipo() == TIPO_ELEM_LADRILLO){
			/* Lo pongo como no vivo, para que no se vea */
			(*it)->setVivo(CONST_FALSE);
		}
	}

	/* Por si acaso seteo en cero la cantidad de ladrillos */
	instance->cantLadrillos = CONST_CERO;
};

/* Elimina el ave si existe en el juego para iniciar sin aves el tramo */
void Partida::resetAve(){
	/* Busco a ralph dentro de la lista de elementos */
	Ave *ave = (Ave *)getElementoById(CONST_ID_AVE);

	/* Ahora le cambio el estado a no viva */
	ave->setVivo(CONST_FALSE);

	/* Piso el nombre por si estaba */
	instance->idAve = CONST_MENOS_UNO;
};

/* Elimina la torta si existe en el juego para iniciar sin tortas el tramo */
void Partida::resetTorta(){
	/* Busco a ralph dentro de la lista de elementos */
	Torta *torta = (Torta *)getElementoById(CONST_ID_TORTA);

	/* Ahora le cambio el estado a no viva */
	torta->setVivo(CONST_FALSE);

	/* Piso el nombre por si estaba */
	instance->idTorta = CONST_MENOS_UNO;
};

